using System;
using _3DTicTacToe.Core;
using Lidgren.Network;

namespace _3DTicTacToe.Net
{
    sealed class TConnection
    {
        private readonly Action<string> _sendMsg;
        private readonly Action _start;
        private readonly Action<string> _shutdown;

        private readonly bool _isHost;
        private readonly Action _stopAccepting;

        public delegate void MsgDlg(NetMessage message);
        public delegate void MsgDlg2();

        public event MsgDlg OnMessageReceived;
        public event MsgDlg OnChatMessageReceived;
        public event MsgDlg OnPlayerInfoReceived;
        public event MsgDlg OnGameTurnReceived;

        public event MsgDlg2 OnPlayerInfoAckReceived;
        public event MsgDlg2 OnGameStarts;

        public bool IsHost
        {
            get { return _isHost; }
        }

        void ClientOnStatusChanged(NetConnectionStatus status)
        {
            ClientStatus = status;
        }

        public NetConnectionStatus ClientStatus { get; private set; }

        private void HandleIncomingMsg(string data)
        {
            var msg = NetMessage.Deserialize(data);
            switch (msg.Header)
            {
                case "chat":
                    Log.Add("Chatmessage received", "TConnection");
                    if (OnChatMessageReceived != null) OnChatMessageReceived(msg);
                    break;
                case "player-info":
                    Log.Add("Player info received", "TConnection");
                    if (OnPlayerInfoReceived != null) OnPlayerInfoReceived(msg);
                    break;
                case "turn":
                    Log.Add("Game turn received", "TConnection");
                    if (OnGameTurnReceived != null) OnGameTurnReceived(msg);
                    break;
                case "ack-player-info":
                    Log.Add("Received acknowledgement for sent player info", "TConnection");
                    if (IsHost) _stopAccepting(); // tell TServer to stop accepting connections
                    if (OnPlayerInfoAckReceived != null) OnPlayerInfoAckReceived();
                    break;
                case "game-starting":
                    Log.Add("Game appears to start...");
                    if (OnGameStarts != null) OnGameStarts();
                    break;
                default:
                    Log.Add("Unknown message received. Header is: " + msg.Header, "TConnection");
                    if (OnMessageReceived != null) OnMessageReceived(msg);
                    break;
            }
        }

        #region public interface

        public void SendMessage(NetMessage msg)
        {
            var data = msg.Serialize();
            _sendMsg(data);

            Log.Add("Queued message '" + msg.Header + "' for sending", "TConnection");
        }

        public void Start()
        {
            _start();
        }

        public void Shutdown(string reason)
        {
            _shutdown(reason);
        }

        // static 

        public static TConnection CreateServer(int port)
        {
            var server = new TServer(port);
            var connection = new TConnection(server);

            return connection;
        }

        public static TConnection CreateClient(string host, int port)
        {
            var client = new TClient(host, port);
            var connection = new TConnection(client);

            return connection;
        }

        #endregion

        TConnection(TClient client)
        {
            _sendMsg = client.SendMessage;
            _start = client.Connect;
            _shutdown = client.Shutdown;
            client.OnDataReceived += HandleIncomingMsg;
            client.OnStatusChanged += ClientOnStatusChanged;
        }

        TConnection(TServer server)
        {
            _isHost = true;
            _stopAccepting = server.StopAccepting;

            _sendMsg = server.SendMessage;
            _start = server.Start;
            _shutdown = server.Shutdown;
            server.OnDataReceived += HandleIncomingMsg;
        }
    }
}